package com.qgs.secsgem.secs.eq.runnable;

import cn.hutool.core.util.HexUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.qgs.secsgem.mq.entity.CallbackMessageEntity;
import com.qgs.secsgem.mq.entity.MQMessage;
import com.qgs.secsgem.mq.entity.common.Header;
import com.qgs.secsgem.mq.producer.component.RabbitSender;
import com.qgs.secsgem.secs.SecsCommunicator;
import com.qgs.secsgem.secs.SecsException;
import com.qgs.secsgem.secs.SecsMessage;
import com.qgs.secsgem.secs.entity.common.*;
import com.qgs.secsgem.secs.gem.*;
import com.qgs.secsgem.secs.hsms.HsmsConnectionMode;
import com.qgs.secsgem.secs.hsmsss.HsmsSsCommunicator;
import com.qgs.secsgem.secs.hsmsss.HsmsSsCommunicatorConfig;
import com.qgs.secsgem.secs.secs2.Secs2;
import com.qgs.secsgem.secs.secs2.Secs2Exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;




/**
 * @Desc: ""
 * @Author: caixiang
 * @DATE: 2023/4/12 11:01
 * 步骤：
 *      1.ctl+c 复制 PID10Runnable  ==>   PID4Runnable
 *      2.进入PID4Runnable 更改 进入PID4Runnable.VID_Type、PID4Runnable.ECID_Type
 *      3.(可选)在comm.addSecsCommunicatableStateChangeListener 里面配置 当新建立tcp连接后，是否是在 EAP-Server这里执行 S1F13+S1F17
 */
public class PID10Runnable implements Runnable{

    private static final Logger logger = LoggerFactory.getLogger(PID10Runnable.class);

    private HsmsSsCommunicatorConfig config;

    public static HsmsSsCommunicator comm;

    private RabbitSender rabbitSender;

    private String exchangName;
    private String eapRequestRoutingKey;
    private String mesRequestRoutingKey;
    private String equipmentId;

    public static String VID_Type = "U4";
    public static String ECID_Type = "U4";
    //eventid type
    public static String CEID_Type = "U4";
    //reportid type
    public static String REPORTID_Type = "U4";
    /**
     * 构造函数
     *
     * @param ip/port 线程名称
     */
    public PID10Runnable(String equipmentId, String ip, Integer port, RabbitSender rabbitSender, String exchangName, String eapRequestRoutingKey,String mesRequestRoutingKey, String VID_Type, String ECID_Type) {
        this.equipmentId = equipmentId;
        this.exchangName = exchangName;
        this.eapRequestRoutingKey = eapRequestRoutingKey;
        this.mesRequestRoutingKey = mesRequestRoutingKey;
        this.VID_Type = VID_Type;
        this.ECID_Type = ECID_Type;
        this.rabbitSender = rabbitSender;
        this.config = new HsmsSsCommunicatorConfig();
        this.config.socketAddress(new InetSocketAddress(ip, port));
        this.config.connectionMode(HsmsConnectionMode.ACTIVE);
        //默认sessionId=deviceId=0
        this.config.sessionId(0);
        this.config.isEquip(false);
        this.config.linktest(60.0F);
        this.config.timeout().t3(45.0F);
        this.config.timeout().t5(10.0F);
        this.config.timeout().t6( 5.0F);
        this.config.timeout().t8( 6.0F);
        //这里只是 new一个HsmsSsCommunicator 实例 ，还未open（还未启动）
        comm = HsmsSsCommunicator.newInstance(this.config);

    }

    //依据VID_TYPE 转换相应的VID


    public HsmsSsCommunicator getComm() {
        return comm;
    }

    public String getVidType() {
        return VID_Type;
    }

    private void sendMessageToMQ(CallbackMessageEntity callbackMessageEntity) throws Exception {
        MQMessage mqMessage = callbackMessageEntity.getMqMessage();
        String exchangeName =  callbackMessageEntity.getExchangeName();
        String routingKey =  callbackMessageEntity.getRoutingKey();


        rabbitSender.sendDirect(mqMessage,exchangeName,routingKey);
        logger.info("EAP => MES (EAP_Request), 状态：DC已收到 , 已入MQ , 内容："+ callbackMessageEntity.toString());
    }

    //dc 从MQ 中接收 mes-request msg , 然后发送给EAP ，eap返回结果给dc ，dc 再把mes response 推送给mq 告知mes
    private void sendMessageToMQ(MQMessage mqMessage) throws Exception {
        rabbitSender.sendDirect(mqMessage,this.exchangName,this.mesRequestRoutingKey);
        logger.info("MES => EAP (MES_Response), 状态：DC已收到 , 已入MQ , 内容："+ mqMessage.toString());
    }
    public static void main(String[] args) {
//        String json = "{\"messageType\":\"DATA\",\"strm\":6,\"func\":11,\"wbit\":true,\"sessionId\":1,\"systemBytes\":9931,\"secs2\":{\"f\":\"L\",\"v\":[{\"f\":\"U4\",\"v\":[94]},{\"f\":\"U4\",\"v\":[64]},{\"f\":\"L\",\"v\":[{\"f\":\"L\",\"v\":[{\"f\":\"U4\",\"v\":[2]},{\"f\":\"L\",\"v\":[{\"f\":\"A\",\"v\":\"20191015\"},{\"f\":\"U1\",\"v\":[2]}]}]}]}]}}";
//        JSONObject secsObject = JSONObject.parseObject(json);
//        JSONObject secs2 = secsObject.getJSONObject("secs2");
//        String secs2F = secs2.getString("f");
//        JSONArray v = secs2.getJSONArray("v");
//
//        System.out.println();

//        String ss = "82";
//        String s1 = "A3";
//        String s = "F";
//        byte[] bytes = HexUtil.decodeHex(s);
//
//        System.out.println(parseALCD(s1)); // Output: 2
    }





    private static ALCD parseALCD(String ss) {
        Character c1;
        Character c2;
        if(ss.length()!=2){
            c1 = '0';
            c2 = ss.charAt(0);
        }else {
            c1 = ss.charAt(0);
            c2 = ss.charAt(1);
        }



        Byte s = HexUtil.decodeHex(c1.toString())[0];
        String s1 = byteToBit(s);
        char[] s11 = s1.toCharArray();

        Byte d = HexUtil.decodeHex(c2.toString())[0];
        String d1 = byteToBit(d);
        char[] d11 = d1.toCharArray();

        int[] res = new int[8];
        res[0] = s11[4]=='1'? 1: 0;
        res[1] = s11[5]=='1'? 1: 0;
        res[2] = s11[6]=='1'? 1: 0;
        res[3] = s11[7]=='1'? 1: 0;

        res[4] = d11[4]=='1'? 1: 0;
        res[5] = d11[5]=='1'? 1: 0;
        res[6] = d11[6]=='1'? 1: 0;
        res[7] = d11[7]=='1'? 1: 0;
        String category = "";
        String alarmStatus = "0";
        if(res[0] == 1){
            alarmStatus = "1";
        }
        res[0] = (byte) 0;

        int i = convertByteArrayToInt(res);
        if(i==0){
            category = "Not used";
        }else if(i==1){
            category = "Personal safety";
        }else if(i == 2 ){
            category = "Equipment safety";
        }else if(i == 3 ){
            category = "Parameter control warning";
        }else if(i == 4 ){
            category = "Parameter control error";
        }else if(i == 5 ){
            category = "Irrecoverable error";
        }else if(i == 6 ){
            category = "Equipment status warning";
        }else if(i == 7 ){
            category = "Attention flags";
        }else if(i == 8 ){
            category = "Data integrity";
        }else {
            //如果i>8 那是保留的，就得问 设备商了。
            category = "Other categories";
        }

        ALCD alcd = new ALCD();
        alcd.setAlarmCategory(category);
        alcd.setAlarmState(alarmStatus);
        alcd.setAlcd(ss);
        return alcd;
    }

    public static int convertByteArrayToInt(int[] ints) {
        int res = 0;
        if(ints[0] == 1){
            res+=128;
        }
        if(ints[1] == 1){
            res+=64;
        }
        if(ints[2] == 1){
            res+=32;
        }
        if(ints[3] == 1){
            res+=16;
        }
        if(ints[4] == 1){
            res+=8;
        }
        if(ints[5] == 1){
            res+=4;
        }
        if(ints[6] == 1){
            res+=2;
        }
        if(ints[7] == 1){
            res+=1;
        }

        return res;
    }


    //S6F11 递归遍历 格式化 SECS结构为 标准格式 RecursiveFormatting
    public static List<ValueNode> get(JSONArray argList){
        //Set<ValueNode> set=new LinkedHashSet<>();
        List<ValueNode> set = new ArrayList<>();

        for(int i=0;i<argList.size();i++){
            if(argList.get(i) == null){
                continue;
            }
            JSONObject j = (JSONObject) argList.get(i);
            String type = j.getString("f");
            if(!type.equals("L")){
                set.add(new ValueNode(j.getString("f"),j.get("v").toString()));

            }else {
                //当嵌套里面还有嵌套，，这里顺序错了，修复一下。
                JSONArray array = j.getJSONArray("v");
                set.add(new ValueNode("List",get(array)));
            }
        }
        return set;
    }

    private void sendEapRequestToMQ(Header header, EventBody eventBody) throws Exception {
        //example for mq     -- 开始
        //往MQ 里面发一条 测试数据
        CallbackMessageEntity callbackMessageEntity2 = new CallbackMessageEntity();
        MQMessage mqMessage = new MQMessage();

        mqMessage.setBody(JSONObject.toJSONString(eventBody));
        mqMessage.setHeader(header);
        callbackMessageEntity2.setMqMessage(mqMessage);
        callbackMessageEntity2.setExchangeName(exchangName);
        callbackMessageEntity2.setRoutingKey(eapRequestRoutingKey);
        sendMessageToMQ(callbackMessageEntity2);
        //example for mq     -- 结束
    }

    private void sendEapRequestToMQ(Header header, AlarmBody alarmBody) throws Exception {
        //example for mq     -- 开始
        //往MQ 里面发一条 测试数据
        CallbackMessageEntity callbackMessageEntity2 = new CallbackMessageEntity();
        MQMessage mqMessage = new MQMessage();

        mqMessage.setBody(JSONObject.toJSONString(alarmBody));
        mqMessage.setHeader(header);
        callbackMessageEntity2.setMqMessage(mqMessage);
        callbackMessageEntity2.setExchangeName(exchangName);
        callbackMessageEntity2.setRoutingKey(eapRequestRoutingKey);
        sendMessageToMQ(callbackMessageEntity2);
        //example for mq     -- 结束
    }

    private void sendEapRequestToMQ(Header header, TraceDataBody traceDataBody) throws Exception {
        //example for mq     -- 开始
        //往MQ 里面发一条 测试数据
        CallbackMessageEntity callbackMessageEntity2 = new CallbackMessageEntity();
        MQMessage mqMessage = new MQMessage();

        mqMessage.setBody(JSONObject.toJSONString(traceDataBody));
        mqMessage.setHeader(header);
        callbackMessageEntity2.setMqMessage(mqMessage);
        callbackMessageEntity2.setExchangeName(exchangName);
        callbackMessageEntity2.setRoutingKey(eapRequestRoutingKey);
        sendMessageToMQ(callbackMessageEntity2);
        //example for mq     -- 结束
    }

    private void sendEapRequestToMQ(Header header) throws Exception {
        //example for mq     -- 开始
        //往MQ 里面发一条 测试数据
        CallbackMessageEntity callbackMessageEntity2 = new CallbackMessageEntity();
        MQMessage mqMessage = new MQMessage();
        mqMessage.setHeader(header);
        callbackMessageEntity2.setMqMessage(mqMessage);
        callbackMessageEntity2.setExchangeName(exchangName);
        callbackMessageEntity2.setRoutingKey(eapRequestRoutingKey);
        sendMessageToMQ(callbackMessageEntity2);
        //example for mq     -- 结束
    }

    /**
     * 把byte转为字符串的bit
     */
    public static String byteToBit(byte b) {
        return ""
                + (byte) ((b >> 7) & 0x1) + (byte) ((b >> 6) & 0x1)
                + (byte) ((b >> 5) & 0x1) + (byte) ((b >> 4) & 0x1)
                + (byte) ((b >> 3) & 0x1) + (byte) ((b >> 2) & 0x1)
                + (byte) ((b >> 1) & 0x1) + (byte) ((b >> 0) & 0x1);
    }


    /**
     * 重写run方法
     */
    @Override
    public void run() {
        System.out.println("当前线程："+Thread.currentThread().hashCode());
        try {

            //这个lambda函数 ，是 SecsLogListener 接口的 received函数的实现，
            // 并且通过addSecsLogListener，把这个实现注册到全局的AbstractSecsCommunicator 这个顶层抽象类中的一个成员变量中去
            comm.addSecsLogListener(msg ->{
                //log out message
                System.out.println(msg.toString());
            });

            //当收到SxFy时候的回调
            comm.addSecsMessageReceiveListener(msg -> {
                String json = msg.toJson();

                /* Nothing */
                try {
                    if(msg.wbit()){
                        if (msg.getStream() ==1 && msg.getFunction() ==1){
                            //写业务
                            Header header = new Header("Request","Execute","GeneratePingRequest",equipmentId);
                            sendEapRequestToMQ(header);
                        }
                        else if (msg.getStream() ==1 && msg.getFunction() ==13){
                            //写业务
                        }else if (msg.getStream() ==6 && msg.getFunction() ==1){
                            //
                            //TODO JSON => 标准JSON
                            JSONObject secsObject = JSONObject.parseObject(json);
                            JSONObject secs2 = secsObject.getJSONObject("secs2");
                            JSONArray v = secs2.getJSONArray("v");
                            String trid = "";
                            String tridType = v.getJSONObject(0).getString("f"); //20,3x,5x
                            if(tridType.equals("A")){
                                trid = v.getJSONObject(0).getString("v");
                            }else {
                                trid = v.getJSONObject(0).getJSONArray("v").getInteger(0).toString();
                            }
                            Integer smpln = v.getJSONObject(1).getJSONArray("v").getInteger(0);
                            String stime = v.getJSONObject(2).getString("v");

                            List<ValueNode> svs = new ArrayList<>();

                            Header header = new Header("Request","Event","TraceData",equipmentId);


                            if(v.getJSONObject(3)!=null){
                                //content 才是真正的
                                JSONArray content =  v.getJSONObject(3).getJSONArray("v");
                                svs = get(content);
                            }
                            TraceDataBody traceDataBody = new TraceDataBody(trid,tridType,smpln,stime,svs);
                            //发送消息到MQ
                            sendEapRequestToMQ(header, traceDataBody);

                        }
                        else if (msg.getStream() ==5 && msg.getFunction() ==1){
                            //alarm report
                            JSONObject secsObject = JSONObject.parseObject(json);
                            JSONObject secs2 = secsObject.getJSONObject("secs2");
                            JSONArray v = secs2.getJSONArray("v");
                            Byte alcd = v.getJSONObject(0).getJSONArray("v").getByte(0);
                            ALCD alcd1 = parseALCD(alcd.toString());

                            Integer alid = v.getJSONObject(1).getJSONArray("v").getInteger(0);

                            String altx_v =v.getJSONObject(2).getString("v");


                            AlarmBody body = new AlarmBody(alcd1,alid,altx_v);
                            Header header = new Header("Request","Alarm","AlarmReport",equipmentId);
                            sendEapRequestToMQ(header,body);

                        }
                        else if (msg.getStream() ==6 && msg.getFunction() ==11){

                            //TODO JSON => 标准JSON
                            JSONObject secsObject = JSONObject.parseObject(json);
                            JSONObject secs2 = secsObject.getJSONObject("secs2");
                            JSONArray v = secs2.getJSONArray("v");
                            Integer ceid = v.getJSONObject(1).getJSONArray("v").getInteger(0);
                            List<ReportNode> rptList = new ArrayList<>();
                            EventBody eventBody = new EventBody(ceid.toString(),rptList);
                            Header header = new Header("Request","Event","EventReport",equipmentId);


                            if(v.getJSONObject(2)!=null){
                                //content 才是真正的
                                JSONArray content =  v.getJSONObject(2).getJSONArray("v");

                                for(int i=0;i<content.size();i++){
                                    ReportNode report = null;
                                    JSONObject reportObj = content.getJSONObject(i);
                                    if(reportObj!=null){
                                        JSONArray array = reportObj.getJSONArray("v");
                                        String rptIdType = array.getJSONObject(0).getString("f");
                                        String rptId = array.getJSONObject(0).getJSONArray("v").getInteger(0).toString();
                                        JSONArray relContent = array.getJSONObject(1).getJSONArray("v");
                                        List<ValueNode> valueNodes = get(relContent);
                                        report = new ReportNode(rptId,rptIdType,valueNodes);
                                        rptList.add(report);
                                    }
                                }
                                System.out.println();
                            }

                            //发送消息到MQ
                            sendEapRequestToMQ(header, eventBody);

                        }
                    }else {
                    }

                    //统一回复
                    response(comm, msg);
                }catch (Exception e){
                    e.printStackTrace();
                    logger.info(e.getMessage());
                }
            });

            comm.addLinktestReceiveListener(lst->{
                System.out.println("这里监听 pid10 linktest lst:"+lst.toString());
            });

            //这里的通讯状态 state 只有true、false, true= SELECTED状态
            comm.addSecsCommunicatableStateChangeListener(state -> {
                //这里主要是做一些初始化的动作。S1F13+S1F17+...
//                try {
//
//                    if ( state /* SELECTED */ ) {
//                        System.out.println("PID10 state："+state);
//                        //向设备发起S1F13事件，请求建立通联连接
//                        {
//                            /* build <L[0] > */
//                            Secs2 ss = Secs2.list(
//                                    Secs2.int4(1)         //MDLN
//                                    , Secs2.int4(1001)    //SOFTREV
//                            );
//
//                            /* send s1f13 <L[0] > */
//                            comm.send(1, 13, true, ss);
//                        }
//
//
//                    }
//                } catch (InterruptedException | SecsException ignore ) {
//                    ignore.printStackTrace();
//                    logger.info("出现中断异常 ："+ignore.getMessage());
//                }


            });

            comm.open();


            synchronized ( PID10Runnable.class ) {
                PID10Runnable.class.wait();
            }
        } catch ( Throwable e ) {
            //把它置为空，到时候就会自动被GC回收
            comm = null;
            e.printStackTrace();
        }


    }

    private static void response(SecsCommunicator comm, SecsMessage msg)
            throws InterruptedException, SecsException {

        int strm = msg.getStream();
        int func = msg.getFunction();
        boolean wbit = msg.wbit();
        Secs2 secs2 = msg.secs2();
        try {
             if(wbit){
                 switch ( strm ) {
                     case 1: {

                         switch ( func ) {
                             case 1: {

                                 if ( wbit ) {
                                     comm.gem().s1f2(msg);
                                 }
                                 break;
                             }
                             case 13: {

                                 if ( wbit ) {
                                     comm.gem().s1f14(msg, COMMACK.OK);
//								comm.gem().s1f1();
                                 }
                                 break;
                             }
                             case 15: {

                                 if ( wbit ) {
                                     comm.gem().s1f16(msg);
                                 }
                                 break;
                             }
                             case 17: {

                                 if ( wbit ) {
                                     comm.gem().s1f18(msg, ONLACK.OK);
                                 }
                                 break;
                             }
                             default: {

                                 if ( wbit ) {
                                     comm.send(msg, strm, 0, false);
                                 }

                                 comm.gem().s9f5(msg);
                             }
                         }

                         break;
                     }

                     case 2: {

                         switch ( func ) {

                             case 21: {

                                 String rcmd = secs2.getAscii();
                                 System.out.println(rcmd);

                                 if ( wbit ) {
                                     comm.gem().s2f22(msg, CMDA.CommandDoesNotExist);
                                 }
                                 break;
                             }
                             case 31: {

                                 String time = secs2.getAscii();
                                 System.out.println(time);

                                 if ( wbit ) {
                                     comm.gem().s2f32(msg, TIACK.OK);
                                 }
                                 break;
                             }
                             default: {

                                 if ( wbit ) {
                                     comm.send(msg, strm, 0, false);
                                 }

                                 comm.gem().s9f5(msg);
                             }
                         }

                         break;
                     }
                     case 5: {

                         switch ( func ) {

                             case 1: {

                                 if ( wbit ) {
                                     comm.gem().s5f2(msg, ACKC5.OK);
                                 }
                                 break;
                             }
                             default: {

                                 if ( wbit ) {
                                     comm.send(msg, strm, 0, false);
                                 }

                                 comm.gem().s9f5(msg);

                             }
                         }

                         break;
                     }
                     case 6: {

                         switch ( func ) {
                             case 1: {

                                 if ( wbit ) {
                                     comm.gem().s6f2(msg, ACKC6.OK);
                                 }
                                 break;
                             }
                             case 11: {

                                 if ( wbit ) {
                                     comm.gem().s6f12(msg, ACKC6.OK);
                                 }
                                 break;
                             }
                             default: {

                                 if ( wbit ) {
                                     comm.send(msg, strm, 0, false);
                                 }

                                 comm.gem().s9f5(msg);
                             }
                         }

                         break;
                     }
                     case 10: {

                         switch ( func ) {
                             case 3: {

                                 /*
                                  * Terminal Display, Single-Block
                                  * format
                                  * S10F3 W
                                  * <L
                                  *   <B[1] TID>
                                  *   <A[n] display-text>
                                  * >
                                  */

                                 String dispText = secs2.getAscii(1);

                                 System.out.println(dispText);

                                 if ( wbit ) {
                                     comm.gem().s10f4(msg, ACKC10.AcceptedForDisplay);
                                 }
                                 break;
                             }
                             case 5: {

                                 /*
                                  * Terminal Display, Multi-Block
                                  * format:
                                  * S10F5 W
                                  * <L
                                  *   <B[1] TID>
                                  *   <L[n]
                                  *     <A block-1>
                                  *     <A block-2>
                                  *     ...
                                  *   >
                                  * >
                                  */

                                 Secs2 ss = secs2.get(1);

                                 /* AbstractSecs2 implements Iterable */
                                 for ( Secs2 s : ss ) {
                                     String dispText = s.getAscii();
                                     System.out.println(dispText);
                                 }

                                 if ( wbit ) {
                                     comm.gem().s10f6(msg, ACKC10.AcceptedForDisplay);
                                 }

                                 break;
                             }
                             default: {

                                 if ( wbit ) {
                                     comm.send(msg, strm, 0, false);
                                 }

                                 comm.gem().s9f5(msg);
                             }
                         }

                         break;
                     }

                     default: {

                         if ( wbit ) {
                             comm.send(msg, strm, 0, false);
                         }

                         comm.gem().s9f3(msg);
                     }
                 }
             }

        } catch ( Secs2Exception e ) {

            comm.gem().s9f7(msg);
        }
    }
}
